package com.lixueju.security.box.validate.code.impl;

import com.alibaba.fastjson.JSONObject;
import com.lixueju.security.box.core.properties.SecurityBoxConstants;
import com.lixueju.security.box.core.support.HttpHelper;
import com.lixueju.security.box.core.support.SecurityBoxHttpServletRequestWrapper;
import com.lixueju.security.box.validate.code.ValidateCode;
import com.lixueju.security.box.validate.code.ValidateCodeException;
import com.lixueju.security.box.validate.code.ValidateCodeRepository;
import com.lixueju.security.box.validate.code.ValidateCodeType;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.bind.ServletRequestUtils;
import org.springframework.web.context.request.ServletWebRequest;

import java.util.concurrent.TimeUnit;

/**
 * 基于redis的验证码存取器，避免由于没有session导致无法存取验证码的问题
 * 1.对于无session的移动端，需要在请求头中携带deviceId参数。
 *
 * @author lixueju
 * @since 2019/10/5 11:56
 **/
@Component
public class SecurityBoxValidateCodeRepository implements ValidateCodeRepository {
    @Autowired
    private RedisTemplate<Object, Object> redisTemplate;

    /*
     * (non-Javadoc)
     *
     * @see
     * com.lixueju.security.core.validate.code.ValidateCodeRepository#save(org.
     * springframework.web.context.request.ServletWebRequest,
     * com.lixueju.security.core.validate.code.ValidateCode,
     * com.lixueju.security.core.validate.code.ValidateCodeType)
     */
    @Override
    public void save(ServletWebRequest request, ValidateCode code, ValidateCodeType type) {
        redisTemplate.opsForValue().set(buildKey(request, type), code, 30, TimeUnit.MINUTES);
    }

    /*
     * (non-Javadoc)
     *
     * @see
     * com.lixueju.security.core.validate.code.ValidateCodeRepository#get(org.
     * springframework.web.context.request.ServletWebRequest,
     * com.lixueju.security.core.validate.code.ValidateCodeType)
     */
    @Override
    public ValidateCode get(ServletWebRequest request, ValidateCodeType type) {
        Object value = redisTemplate.opsForValue().get(buildKey(request, type));
        if (value == null) {
            return null;
        }
        return (ValidateCode) value;
    }

    /*
     * (non-Javadoc)
     *
     * @see
     * com.lixueju.security.core.validate.code.ValidateCodeRepository#remove(org.
     * springframework.web.context.request.ServletWebRequest,
     * com.lixueju.security.core.validate.code.ValidateCodeType)
     */
    @Override
    public void remove(ServletWebRequest request, ValidateCodeType type) {
        redisTemplate.delete(buildKey(request, type));
    }

    /**
     * @param request
     * @param type
     * @return
     */
    private String buildKey(ServletWebRequest request, ValidateCodeType type) {
        String keyId;
        if (type.toString().toLowerCase().equals("sms")) {
            // 如果是短信验证，需要验证对应的手机号
            if (request.getRequest().getMethod().equals("GET")) {
                // 发送验证码
                try {
                    keyId = ServletRequestUtils.getStringParameter(request.getRequest(),
                            SecurityBoxConstants.DEFAULT_PARAMETER_NAME_MOBILE);
                } catch (ServletRequestBindingException e) {
                    throw new ValidateCodeException("请携带" + SecurityBoxConstants.DEFAULT_PARAMETER_NAME_MOBILE + "参数");
                }
            } else {
                // 校验验证码
                SecurityBoxHttpServletRequestWrapper securityBoxBodyWrapper = (SecurityBoxHttpServletRequestWrapper) request.getRequest();
                String body = HttpHelper.getBodyString(securityBoxBodyWrapper);
                keyId = JSONObject.parseObject(body).getString(SecurityBoxConstants.DEFAULT_PARAMETER_NAME_MOBILE);
                if (StringUtils.isBlank(keyId)) {
                    throw new ValidateCodeException("请在请求体携带" + SecurityBoxConstants.DEFAULT_PARAMETER_NAME_MOBILE + "参数");
                }
            }
        } else {
            keyId = request.getSessionId();
            if (StringUtils.isBlank(keyId)) {
                keyId = request.getHeader("deviceId");
            }
            if (StringUtils.isBlank(keyId)) {
                throw new ValidateCodeException("请在请求头中携带deviceId参数");
            }
        }
        return "code:" + type.toString().toLowerCase() + ":" + keyId;
    }
}
